package utils

import (
	"MoSkeleton/framework/core/consts"
	"MoSkeleton/framework/core/moerrors"
	"fmt"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"mime/multipart"
	"reflect"
	"strings"
)

func GetRequestIp(c *gin.Context) string {
	if c == nil {
		return consts.GinUnknow_Ip
	}

	ip := c.GetHeader("x-forwarded-for")

	if len(ip) == 0 || consts.GinUnknow_Ip == ip {
		ip = c.GetHeader("Proxy-Client-IP")
	}
	if len(ip) == 0 || consts.GinUnknow_Ip == ip {
		ip = c.GetHeader("X-Forwarded-For")
	}
	if len(ip) == 0 || consts.GinUnknow_Ip == ip {
		ip = c.GetHeader("WL-Proxy-Client-IP")
	}
	if len(ip) == 0 || consts.GinUnknow_Ip == ip {
		ip = c.GetHeader("X-Real-IP")
	}

	if len(ip) == 0 || consts.GinUnknow_Ip == ip {
		ip = c.RemoteIP()
	}

	if "0:0:0:0:0:0:0:1" == ip {
		return "127.0.0.1"
	} else {
		return getMultistageReverseProxyIp(ip)
	}
}

func GinGet(c *gin.Context, key string, t any) {
	val, exists := c.Get(key)
	if !exists {
		return
	}
	getFromValue(val, t)
}

func GinMustGet(c *gin.Context, key string, t any) {
	val := c.MustGet(key)
	getFromValue(val, t)
}

func GinPrimitiveGetDef[T int | int32 | int64 | uint64 | uint | float32 | float64](c *gin.Context, key string, defVal T) T {
	val, exists := c.Get(key)
	if !exists {
		return defVal
	}
	return val.(T)
}

func GinPrimitiveMustGetDef[T int | int32 | int64 | uint64 | uint | float32 | float64](c *gin.Context, key string) T {
	val := c.MustGet(key)
	return val.(T)
}

func GinLimitUploadFileSize(c *gin.Context, filename string, maxFileSizeConfig string) (*multipart.FileHeader, error) {
	maxFileSize, err := ConvertToSizeInt64(maxFileSizeConfig)
	if err != nil {
		MoLogger.Error("上传文件失败", zap.Error(err))
		return nil, moerrors.NewValidateMsgError(consts.ValidatorParamsCheckFailCode, "上传文件失败", err)
	}
	file, err := c.FormFile(filename)
	if err != nil {
		MoLogger.Error("上传文件失败", zap.Error(err))
		return nil, moerrors.NewValidateMsgError(consts.ValidatorParamsCheckFailCode, "上传文件失败", err)
	}
	if file.Size > maxFileSize {
		MoLogger.Error("上传文件失败: ", zap.Int64("filesize", file.Size))
		return nil, moerrors.NewValidateError(consts.FilesUploadMoreThanMaxSizeCode, "上传文件太大")
	}
	return file, nil
}

// 从多级反向代理中获得第一个非unknown IP地址
// param ip 获得的IP地址
// return 第一个非unknown IP地址
func getMultistageReverseProxyIp(ip string) string {
	// 多级反向代理检测
	if ip != "" && strings.Contains(ip, ",") {
		ips := strings.Split(strings.TrimSpace(ip), ",")
		for _, subIp := range ips {
			if !isUnknown(subIp) {
				ip = subIp
				break
			}
		}
	}
	return stringLimit(ip, 255)
}

// 判断 IP 地址是否为 unknown
func isUnknown(ip string) bool {
	return ip == "" || ip == "unknown" || ip == "0.0.0.0"
}

// 限制字符串长度
func stringLimit(s string, maxLength int) string {
	if len(s) > maxLength {
		return s[:maxLength]
	}
	return s
}

func getFromValue(val any, t any) {
	tVal := reflect.ValueOf(t)
	if tVal.Kind() != reflect.Ptr || tVal.IsNil() {
		fmt.Println("t is not a valid pointer")
		return
	}
	// 获取 t 指向的类型
	tElemType := tVal.Type().Elem()

	// 尝试将 val 赋值给 t
	if valVal := reflect.ValueOf(val); valVal.Type().ConvertibleTo(tElemType) {
		tVal.Elem().Set(valVal.Convert(tElemType))
	} else {
		fmt.Println("value type does not match target type")
	}
}
